package com.espirit.finance.core;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;

import com.espirit.eap.util.BigDecimalUtil;
import com.googlecode.cswish.util.bean.ModelUtil;

@Service
public class CalcAcctGeneralStrategy implements IAcctCalcStrategy{
	private static final Log logger = LogFactory.getLog(CalcAcctGeneralStrategy.class);
	
	@Resource
	AcctItemCalcService acctItemCalcService;
	
	
	@Override
	public BigDecimal getAmount(TicketAware ticketAware,
			AcctItem acctItem) {
		Integer style = acctItem.getStyle();
		if (AcctItem.STRATEGY_STYLE_PROPERTY == style){
			BigDecimal bigValue = getAcctItemRefAmount(ticketAware, acctItem.getAcctItemRef());
			return acctItemCalcService.getFactorAmount(acctItem, ticketAware, bigValue);
		} else if (AcctItem.STRATEGY_STYLE_CASCADE == style){
			BigDecimal value = calcScopeAmount(ticketAware, acctItem, false);
			return value;
		} else if (AcctItem.STRATEGY_STYLE_CASCADE_SUM == style){
			BigDecimal value = calcScopeAmount(ticketAware, acctItem, true);
			return value;
		}
		
		return null;
	}
	
	private BigDecimal getAcctItemRefAmount(TicketAware ticketAware, AcctItemRef acctItemRef){
		Object value = null;
		if (acctItemRef != null){
			value = ModelUtil.getProperty(ticketAware, acctItemRef.getProperty());
		} 
		BigDecimal bigValue = BigDecimalUtil.getBigDecimalValue(value);
		return bigValue;
	}
	
	private BigDecimal calcScopeAmount(TicketAware ticketAware,
			AcctItem acctItem, boolean sum){
		List<AcctItemScope>  acctItemScopes = acctItem.getAcctItemScopes();
		if (acctItemScopes == null || acctItemScopes.size() == 0){
			logger.error("请定义价位区间:" + acctItem.getName());
			return null;
		}
		
		if (acctItem.getAcctItemRef() == null){
			logger.error("请定义属性:" + acctItem.getName());
			return null;
		}
		
		String property = acctItem.getAcctItemRef().getProperty();
		
		Object value = ModelUtil.getProperty(ticketAware, property);
		
		if (value instanceof Collection){
			Collection values = (Collection) value;
			BigDecimal amount = null;
			for(Object obj : values){
				BigDecimal left = BigDecimalUtil.getBigDecimalValue(obj);
				if (left != null){
					BigDecimal target = calcAmount(left, ticketAware, acctItem, sum);
					if (target != null){
						if (amount == null){
							amount = BigDecimal.ZERO;
						}
						amount = amount.add(target);
					}
				}
			}
			return amount;
		} else {
			BigDecimal left = BigDecimalUtil.getBigDecimalValue(value);
			if (left != null){
				return calcAmount(left, ticketAware, acctItem, sum);
			}
		}
		return null;
	}

	private BigDecimal calcAmount(BigDecimal scope, TicketAware ticketAware,
			AcctItem acctItem , boolean sum){
		List<AcctItemScope>  acctItemScopes = acctItem.getAcctItemScopes();
		if (acctItemScopes == null || acctItemScopes.isEmpty()) return null;
		
		BigDecimal amount = null;
		BigDecimal left = scope;
		
		for(AcctItemScope acctItemScope : acctItemScopes){
			BigDecimal price = acctItemScope.getPrice();
			BigDecimal to = acctItemScope.getTo();
			BigDecimal from = acctItemScope.getFrom();
			
			if (from == null){
				from = BigDecimal.ZERO;
			}
			if (scope.compareTo(from) < 0){
				break;
			}
			
			if (to == null){
				to = BigDecimal.valueOf(Integer.MAX_VALUE);
			}
			
			int result = scope.compareTo(to); 
			
			if (result <= 0){
				if (sum){
					price = getScopeAmount(price, left, acctItemScope, acctItem, ticketAware);
					if (price != null){
						if (amount == null){
							amount = BigDecimal.ZERO;
						}
						amount = amount.add(price);
					}
				} else {
					amount = getScopeAmount(price, scope, acctItemScope, acctItem, ticketAware);
				}
				break;
			} else {
				if (sum){
					left = scope.subtract(to);
					price = getScopeAmount(price, to.subtract(from), acctItemScope, acctItem, ticketAware);
					if (price != null){
						if (amount == null){
							amount = BigDecimal.ZERO;
						}
						amount = amount.add(price);
					}
				} 
			}
		}
		return amount;
	}
	
	private BigDecimal getScopeAmount(BigDecimal price, BigDecimal base, AcctItemScope acctItemScope, AcctItem acctItem, TicketAware ticketAware){
		if (acctItemScope != null) {
			Integer style = acctItemScope.getStyle();
			if (style == null) {
				style = AcctItemScope.STYLE_PER_PRICE;
			}
			if (style == AcctItemScope.STYLE_PER_PRICE){
				if (price != null && base != null){
					BigDecimal value = price.multiply(base);
					value = acctItemCalcService.getScopeAmount(acctItem, acctItemScope, ticketAware, value);
					return value;
				}  else {
					return null;
				}
			} else if (style == AcctItemScope.STYLE_PROPERTY) {
				AcctItemRef acctItemRef = null;
				if (acctItemScope.getAcctItemRef() != null){
					acctItemRef = acctItemScope.getAcctItemRef();
				} else {
					if (acctItem != null && acctItem.getAcctItemRef() != null){
						acctItemRef = acctItem.getAcctItemRef();
					}
				}
				BigDecimal value = getAcctItemRefAmount(ticketAware, acctItemRef);
				return acctItemCalcService.getFactorAmount(acctItem, ticketAware, value);
			} else {
				return price;
			}
		} else {
			return null;
		}
	}
}
